
// "Copyright [2021] <Copyright Xinchi Han>"

#include "Mmu.h"
#include <string.h>
#include <vector>
#include "inet/node/inet/Router.h"
#include "inet/common/packet/Packet_m.h"

namespace inet {
Define_Module(Mmu);

void Mmu::initialize() {
    num_ports_ = par("numPorts");
    pause_unit_ = par("pauseUnit");
    mmu_manager_.reset(new MmuManager(num_ports_));
    std::vector<int> bandwidth_gbps;
    std::vector<int> link_delay_ns;
    for (int gate_idx = 0; gate_idx < num_ports_; ++gate_idx) {
        bandwidth_gbps.push_back(getBandwidth(gate_idx));
        link_delay_ns.push_back(getLinkDelay(gate_idx));
    }
    mmu_manager_->ConfigMmu(bandwidth_gbps, link_delay_ns);
}

void Mmu::handleMessage(cMessage *msg) {
    int ind = msg->getArrivalGate()->getIndex();
    // std::cout << "MMU message arrived at gate " << msg->getArrivalGate()->getFullName() << endl;
    if (msg->arrivedOn("upperLayerIn", ind)){
        if(msg->isPacket()){
            Packet* packet = check_and_cast<Packet *>(msg);
            int msg_length_byte = packet->getTotalLength().get() / 8;
            //从IP层发出时开始占用出端口
            mmu_manager_->UpdateEgressAdmission(ind, msg_length_byte);
            send(msg, "lowerLayerOut", ind);
        }
        else
        {
            send(msg, "lowerLayerOut", ind);
        }
    } else if (msg->arrivedOn("lowerLayerIn",ind)) {
        if(msg->isPacket()) {
            Packet* packet = check_and_cast<Packet *>(msg);
            int msg_length_byte = packet->getTotalLength().get() / 8;
            if (mmu_manager_->CheckIngressAdmission(ind, msg_length_byte)) {
                mmu_manager_->UpdateIngressAdmission(ind, msg_length_byte);
                // 在packet从链路层往上发时,加tag记录包的发送方mac地址以及接收的端口,便于提供Resume操作需要的metadata
                // PfcInfoReq *pfc_info_tag = packet->addTagIfAbsent<PfcInfoReq>();
                // pfc_info_tag->setFromAddress(packet->findTag<PfcInfoReq>()->getFromAddress());
                // pfc_info_tag->setInPort(ind);
                packet->addTagIfAbsent<PfcInfoReq>()->setFromAddress(packet->findTag<PfcInfoReq>()->getFromAddress());
                packet->addTagIfAbsent<PfcInfoReq>()->setInPort(ind);
                send(msg, "upperLayerOut", ind);
                // 检查是否需要发送pause帧
                auto *parent = getParentModule();
                Router *router = dynamic_cast<Router *>(parent);
                if (mmu_manager_->CheckShouldPause(ind) && router->enablePFC) {
                    mmu_manager_->SetPause(ind);
                    sendPause(ind, packet->findTag<PfcInfoReq>()->getFromAddress(),  pause_unit_);
                }
            }
        }
        else {
            send(msg, "upperLayerOut", ind);
        }
    } else {
        error("something wrong in Mmu::handleMessage");
    }
}

// 发送Pause帧
void Mmu::sendPause(int ind, const MacAddress& mac_address_arrival_gate, int pause_unit) {
    Enter_Method("sendPause");
    Ieee802PauseCommand *ctrl = new Ieee802PauseCommand();
    ctrl->setDestinationAddress(mac_address_arrival_gate);
    EV << "getMacAddress: " << "\t" << mac_address_arrival_gate << std::endl;
    auto msg = new Request();
    ctrl->setPauseUnits(pause_unit);
    msg->setControlInfo(ctrl);
    EV_INFO << "Sending Pause Frame " << ctrl->getPauseUnits()
            << "  " << mac_address_arrival_gate << "\n";
    send(msg, "lowerLayerOut", ind);
}

void Mmu::sendResume(int ind, const MacAddress& mac_address_arrival_gate) {
    sendPause(ind, mac_address_arrival_gate, 0);
}

// 检查是否需要发送pause帧,如果是则发送
void Mmu::CheckAndSendResume(int ind, const MacAddress& macAddressArrivalGate) {
    if (mmu_manager_->CheckShouldResume(ind)) {
        mmu_manager_->SetResume(ind);
        sendResume(ind, macAddressArrivalGate);
    }
}

void Mmu::handleRegisterService(const Protocol& protocol, cGate *out, ServicePrimitive servicePrimitive) {
    Enter_Method("handleRegisterService");
    int index = out->getIndex();
    const char* gateName = out->getName();
    if (strcmp(gateName, "upperLayerOut") == 0) {
        EV << "service registration from upper layer: " << protocol
           << " " << out->getFullName() << endl;
        registerService(protocol, gate("lowerLayerIn", index), servicePrimitive);
    } else if (strcmp(gateName, "lowerLayerOut") == 0) {
        EV << "service registration from lower layer: " << protocol
           << " " << out->getFullName() << endl;
        registerService(protocol, gate("upperLayerIn", index), servicePrimitive);
    } else {
        error("something wrong in MMU::handleRegisterService");
    }
}

void Mmu::handleRegisterProtocol(const Protocol& protocol, cGate *in, ServicePrimitive servicePrimitive) {
    Enter_Method("handleRegisterProtocol");
    int index = in->getIndex();
    const char* gateName = in->getName();
    if (strcmp(gateName, "upperLayerIn") == 0) {
        EV << "protocol registration from upper layer: " << protocol
           << " " << in->getFullName() << endl;
        registerProtocol(protocol, gate("lowerLayerOut", index), servicePrimitive);
    } else if (strcmp(gateName, "lowerLayerIn") == 0) {
        EV << "protocol registration from lower layer: " << protocol
           << " " << in->getFullName() << endl;
        registerProtocol(protocol, gate("upperLayerOut", index), servicePrimitive);
    } else {
        error("something wrong in MMU::handleRegisterProtocol");
    }
}

void Mmu::handleRegisterInterface(const InterfaceEntry &interface, cGate *out, cGate *in) {
    Enter_Method("handleRegisterInterface");
    int index = out->getIndex();
    const char* gateName = out->getName();
    if (strcmp(gateName, "upperLayerOut") == 0) {
        EV << "Interface registration from upper layer: " << interface
           << " " << out->getFullName() << endl;
        registerInterface(interface, gate("lowerLayerIn", index), gate("lowerLayerOut", index));
    } else if (strcmp(gateName, "lowerLayerOut") == 0) {
        EV << "Interface registration from lower layer: " << interface
           << " " << out->getFullName() << endl;
        registerInterface(interface, gate("upperLayerIn", index), gate("upperLayerOut", index));
    } else {
        error("something wrong in MMU::handleRegisterInterface");
    }
}


}  // namespace inet